
#include "mm.h"
#include "string.h"
#include <iostream>

using namespace WtfEngine;

GcObject::~GcObject()
{
#ifndef NCHECKS
	if(mlRefCount != 0)
		throw std::logic_error(String("Freeing referenced object!! Type: ") + this->GcGetClass());
#endif
	
	MemoryManager::GetInstance().DeregisterObject(this);
};

void GcObject::GcReferenceObject() const {
	mlRefCount++;
};

bool GcObject::GcDereferenceObject() const {
	assert(mlRefCount > 0);
	mlRefCount--;
	return ((mlRefCount == 0) && mvWeakRef.empty());
};

void GcObject::GcAddWeakRef(const WeakRef<IGcObject>* pRef) const {
	WeakRefWrapper r(pRef);

#ifndef NDEBUG
	assert(std::find(mvWeakRef.begin(), mvWeakRef.end(), r) == mvWeakRef.end());
	assert(r.mpWeakRef);
#endif

	mvWeakRef.push_back(r);

#ifndef NDEBUG
	assert(r.mpWeakRef == NULL);
#endif
};

void GcObject::GcRemoveWeakRef(const WeakRef<IGcObject>* pRef) const {
	mvWeakRef.remove(WeakRefWrapper(pRef));
};



/**** Memory Manager ****/

MemoryManager::MemoryManager()
	: mvpObjects(), mvpSingletons(), mlInvocationCount(0) {
};

MemoryManager::~MemoryManager(){
	ISingleton * pSingleton;
	tSingletonList::iterator i(mvpSingletons.begin());
	while(i != mvpSingletons.end()) {
		pSingleton = *i++;
		if(pSingleton != this)
			delete pSingleton;
	};
};


/**
 * This invokes the garbage collector, instructing it to free a
 * certain amount of memory.
 **/
unsigned MemoryManager::Reclaim(unsigned lAmount)
{
	unsigned				lCurrent, lSize;
	tObjectList::iterator	i(mvpObjects.begin()), next;
	
	if(lAmount == 0)
		lAmount = (unsigned)~0;
	
	for(lCurrent = 0; (lCurrent < lAmount) && (i != mvpObjects.end());){
		if((*i)->GcRefCount() == 0){
			lSize = (*i)->GcGetSize();
			next = i; next++;
			
#ifndef NDEBUG
			std::clog << "GC: Reclaimed object " << *i << ", class " << (*i)->GcGetClass() << ", size " << lSize << std::endl;
#endif
			
			(*i)->GcDelete();
			lCurrent += lSize;
			i = next;
		} else i++;
	};
	
	mlInvocationCount++;
	return lCurrent;
};

void MemoryManager::DumpStatus(){
	unsigned					lObjMem, lSingletonMem, lRefs = 0, lWeakRefs = 0;
	tObjectList::iterator		i(mvpObjects.begin());
	tSingletonList::iterator	j(mvpSingletons.begin());
	
	for(lObjMem = 0; i != mvpObjects.end(); i++) {
		lObjMem += (*i)->GcGetSize();
		lRefs += (*i)->GcRefCount();
		lWeakRefs += (*i)->GcTotalRefCount() - (*i)->GcRefCount();
	};
	for(lSingletonMem = 0; j != mvpSingletons.end(); j++)
		lSingletonMem += (*j)->GcGetSize();
	
	std::cout
		<< "MEM:\n"
		<< "\t# Objects: " << mvpObjects.size() << " ( " << ((float)lObjMem / 1024) << " KB )\n"
		<< "\t# Singletons: " << mvpSingletons.size() << " ( " << ((float)lSingletonMem / 1024) << " KB )\n"
		<< "\t# References: " << lRefs << "\n"
		<< "\t# Weak References: " << lWeakRefs << "\n"
		<< "\t# GC Invocations: " << mlInvocationCount << std::endl
		<< "\tTotal Memory Usage: " << (float)(lObjMem + lSingletonMem) / 1024 << " KB\n";
};


/**** Singleton ****/

ISingleton::ISingleton(){
	if(MemoryManager::isInitialized())
		MemoryManager::GetInstance().RegisterSingleton(this);
};
ISingleton::~ISingleton(){
	if(MemoryManager::GetInstancePtr() != (MemoryManager*)this)
		MemoryManager::GetInstance().DeregisterSingleton(this);
};
